// Copyright (C) Microsoft Corporation. All rights reserved.

using System.Reflection;
using System.Windows.Forms;
using System.Drawing ;
using System;
using System.Collections.ObjectModel;
using System.Security.Permissions;
using System.Collections.Generic;

namespace Microsoft.VSPowerToys.VSCmdShell
{

    /// <summary>
    /// Simple implementation of Intellisense browser like control.
    /// It displays a list of all possible entries a user may type in a list box and provides
    /// continous filtering based on user input.
    /// </summary>
	public class Browser : System.Windows.Forms.Form
	{
	
		#region Windows Form Designer generated code

		
		[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        protected override void WndProc(ref System.Windows.Forms.Message m)
		{
			// defined in winuser.h
			const int WM_ACTIVATEAPP = 0x001C;
			if (m.Msg == WM_ACTIVATEAPP)
			{
				this.Visible = false;
			}
			base.WndProc(ref m);		
		}

		public Browser()
		{
			InitializeComponent();
this.listBox.MouseUp +=new System.Windows.Forms.MouseEventHandler(listBox_MouseUp);
			this.listBox.Sorted = true;

		}

		//Form overrides dispose to clean up the component list.
		protected  override void Dispose( bool disposing) 
		{
			if ( disposing ) 
			{
				if (! (components == null) ) 
				{
					components.Dispose();
				}
			}
			base.Dispose(disposing);
		}

		//Required by the Windows Form Designer
		private System.ComponentModel.IContainer components = null ;

		//NOTE: The following procedure is required by the Windows Form Designer
		//It can be modified using the Windows Form Designer.  
		//do { not modify it using the code editor.
		private System.Windows.Forms.ListBox listBox;
		private System.Windows.Forms.StatusBar statusBar;
		
		private void InitializeComponent()
		{
			this.listBox = new System.Windows.Forms.ListBox();
			this.statusBar = new System.Windows.Forms.StatusBar();
			this.SuspendLayout();
			//
			//listBox
			//
			this.listBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
			this.listBox.Dock = System.Windows.Forms.DockStyle.Fill;
			this.listBox.IntegralHeight = false;
			this.listBox.Location = new System.Drawing.Point(0, 0);
			this.listBox.Name = "listBox";
			this.listBox.Size = new System.Drawing.Size(352, 168);
			this.listBox.TabIndex = 0;
			//
			//statusBar
			//
			this.statusBar.Location = new System.Drawing.Point(0, 168);
			this.statusBar.Name = "statusBar";
			this.statusBar.Size = new System.Drawing.Size(352, 16);
			this.statusBar.TabIndex = 1;
			this.statusBar.Visible = false;
			//
			//Browser
			//
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(352, 184);
			this.Controls.Add(this.listBox);
			this.Controls.Add(this.statusBar);
			this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
			this.Name = "Browser";
			this.ShowInTaskbar = false;
			this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
			this.TopMost = true;
			this.ResumeLayout(false);

		
		}

		#endregion

        #region Event Declerations

        /// <summary>
        /// This event is raised when user selects an entry from the list.
        /// </summary>
        public event EventHandler UserSelected;
		
        #endregion
        
        /// <summary>
        /// Collection of all possible entries
        /// </summary>
        private Collection<string> items = new Collection<string>();

        #region Public Properties

        /// <summary>
        /// Returns the current selected entry. If nothing is selected
        /// empty string is returned
        /// </summary>
        public string  SelectedString  
		{
			get 
			{
                if (this.listBox.SelectedItem != null)
                {
                    return this.listBox.SelectedItem.ToString();
                }
                else
                {
                    return String.Empty;
                }
			}
        }

        /// <summary>
        /// List of all possible entries 
        /// </summary>
        public Collection<string> Items
        {
            get
            {
                return items;
            }
        }

        #endregion

        #region - Methods

        /// <summary>
        /// Clears the user selection
        /// </summary>
        public void ClearSelection()
		{
			listBox.ClearSelected();
		}

        /// <summary>
        /// Moves the selection based on the specified keycode.
        /// </summary>
        /// <param name="keyCode">Key that user pressed</param>
		public void MoveSelection( System.Windows.Forms.Keys keyCode) 
		{
            if (listBox.Items.Count == 0)
            {
                /// Return immediately if there is no item to choose.
                return;
            }
			const int pageStep = 10;
			if (listBox.SelectedIndex == -1)
			{
				listBox.SelectedIndex = listBox.TopIndex;
			}
			else
			{
				switch ( keyCode)
				{
					case Keys.Up :
					case (Keys.Tab | Keys.Shift):
                        if (listBox.SelectedIndex > 0)
                        {
                            listBox.SelectedIndex -= 1;
                        }
                        else
                        {
                            listBox.SelectedIndex = this.listBox.Items.Count - 1;
                        }
						break;
					case Keys.Down:
					case Keys.Tab:
                        if (listBox.SelectedIndex < listBox.Items.Count - 1)
                        {
                            listBox.SelectedIndex += 1;
                        }
                        else
                        {
                            listBox.SelectedIndex = this.listBox.TopIndex;
                        }
						break;
					case Keys.PageDown:
                        if (listBox.SelectedIndex < listBox.Items.Count - pageStep)
                        {
                            listBox.SelectedIndex += pageStep;
                        }
                        else
                        {
                            listBox.SelectedIndex = this.listBox.Items.Count - 1;
                        }
						break;
					case Keys.PageUp:
                        if (listBox.SelectedIndex >= pageStep)
                        {
                            listBox.SelectedIndex -= pageStep;
                        }
                        else
                        {
                            this.listBox.SelectedIndex = this.listBox.TopIndex;
                        }
						break;
				}
			}

		}

        /// <summary>
        /// Filters the list by given prefix and moves to first available selection.
        /// </summary>
        /// <param name="prefix">Command prefix, if null or empty all items are listed</param>
        public void Filter(string prefix)
        {
            lock (this.Items)
            {
                this.listBox.BeginUpdate();
                try
                {
                    this.listBox.Items.Clear();
                    foreach (string item in this.Items)
                    {
                        if (String.IsNullOrEmpty(prefix) || item.StartsWith(prefix,StringComparison.CurrentCultureIgnoreCase))
                        {
                            this.listBox.Items.Add(item);
                        }
                    }
                    if (this.listBox.Items.Count > 0)
                    {
                        this.listBox.SelectedIndex = 0;
                    }
                    else
                    {
                        // Comparison with literal 1 is okay here since 
                        // IShellHost.VisualStudioCommandEscapeCharacter is defined as System.Char.
                        // However, if that property is ever changed to a string, the Filter method
                        // will need to take the value of that property as a parameter so this comparison
                        // can be done against the string's length instead.
                        if (prefix.Length - 1 > 1)  
                            this.Filter(prefix.Substring(0, prefix.Length - 1));
                        else
                            this.Visible = false;
                    }
                }
                finally
                {
                    this.listBox.EndUpdate();
                }
            }
        }

		private void listBox_MouseUp( object sender,  System.Windows.Forms.MouseEventArgs e) 
		{
			if ( e.Button == MouseButtons.Left ) 
			{
				this.Visible = false; //TODO: Verify hide is OK used to be  Close();
				//call event to use the string the user selected
				if (UserSelected != null)
					UserSelected(null, null);
			}
		}

        /// <summary>
        /// Shows the window on the given location.
        /// </summary>
        /// <param name="location">Location to display window</param>
        /// <param name="fontHeightAdjustment">Amount to adjust to show window under the text</param>
        public void ShowBrowser(Point location, int fontHeightAdjustment) 
		{
			//Get rect for entire screen
			
			Rectangle screenSize = SystemInformation.VirtualScreen;			
			//Adjust Y to put browser below the current line with the font height
			location.Y += fontHeightAdjustment;
			if ( location.Y + this.Height > screenSize.Height ) 
			{
				//If the bottem would be off of the screen adjust to put it above the current line
				location.Y -= fontHeightAdjustment + this.Height;
			}
			location.X = Math.Max(Math.Min(screenSize.Right - this.Width, location.X), screenSize.X);
			this.Location = location;
            this.Visible = true;
        }

        #endregion

    }
}